﻿using Ndns.ServerHost.Abstractions;
using Ndns.ServerHost.Abstractions.Services;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Ndns.ServerHost
{
    class DnsServerHost : IDnsServerHost
    {

        private readonly IServiceCollection _serviceCollection;
        private readonly MiddlewareNode _headNode;
        private readonly CancellationTokenSource _disposeToken;


        public DnsServerHost(IServiceCollection serviceCollection, MiddlewareNode headNode)
        {
            _serviceCollection = serviceCollection;
            _headNode = headNode;
            _disposeToken = new CancellationTokenSource();
        }

        public async Task RunAsync(CancellationToken cancellationToken)
        {
            if (_disposeToken.IsCancellationRequested)
            {
                throw new ObjectDisposedException(nameof(DnsServerHost));
            }

            MiddlewareNode head = _headNode;
            MiddlewareNode current;

            // 准备Services
            current = head;
            while (current != null)
            {
                if (current.Next != null)
                {
                    current.Middleware.RegisterServices(_serviceCollection, current.Next.InvokeAsync);
                }
                else
                {
                    current.Middleware.RegisterServices(_serviceCollection, FallbackMiddleware.StaticInvokeAsync);
                }
                current = current.Next;
            }

            // 调用Start
            current = head;
            while (current != null)
            {
                current.Middleware.Start();
                current = current.Next;
            }

            // 等待结束
            var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _disposeToken.Token);
            var shouldExit = new ManualResetEventSlim(false);

            using (var s = linkedCts.Token.Register(() => shouldExit.Set()))
            {
                await shouldExit.WaitHandle.AsTask();
            }

            // 释放资源
            head?.Dispose();
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // 释放托管状态(托管对象)。
                    _disposeToken.Cancel();
                }

                // 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~DnsServerHost() {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // 如果在以上内容中替代了终结器，则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion

    }
}
